const http = require("http");
const crypto = require('crypto');
const request = require('superagent');
import env from '../config/env';
const { view, lensProp, prop } = require('ramda');

interface ISmsApi {
    smsKey: string;
    smsUser: string;
}

interface ISendSms {
    signature?: string;
    timestamp?: string;
    templateId: number;
    msgType?: number;
}

interface ISendTo {
    phone: string;
    vars?: string;
}

interface IXSendTo {
    tos: string; // 手机号和替换变量的对应的json串, 手机号最多不能超过200
}

interface ISendSmsByTemplate extends ISendSms, ISendTo { }

interface ISendXSmsByTemplate extends ISendSms, IXSendTo { }

interface ISendSmsByVoice extends ISendSms {
    code: string;
}

class SmsService {
    public constructor(private API: ISmsApi) { }

    /**
     * 将实际调用API的参数以字母升序(A-Z)排列, 不包括 smsKey 和 signature 字段本身
     * @param dict
     * @returns {{}}
     */
    private sortDict(dict) {
        var dict2 = {},
            keys = Object.keys(dict).sort();
        for (var i = 0, n = keys.length, key; i < n; ++i) {
            key = keys[i];
            dict2[key] = dict[key];
        }
        return dict2;
    }

    /**
     * 按照排列之后的顺序, 以 'key=value' + '&' + 'key=value' 的方式连接所有参数, 得到字符串 param_str
     */
    private chainDict(dict, smsKey) {
        return Object
            .keys(dict)
            .reduce((pre, cur) => {
                return pre + cur + '=' + dict[cur] + '&'
            }, smsKey + '&') + smsKey;
    }

    /**
     * 计算 sign_str 的MD5值 (32位, 不区分大小写), 得到 signature
     * @param string
     * @returns {any}
     */
    private genMd5(string) {
        return crypto
            .createHash('md5')
            .update(string)
            .digest('hex');
    }

    public async sendSmsByTemplate(params: ISendSmsByTemplate | ISendXSmsByTemplate) {
        Object.assign(params, this.API);

        if (!params.signature) {
            const signature = this.genMd5(this.chainDict(this.sortDict(params), this.API.smsKey)).toUpperCase();
            Object.assign(params, { signature: signature });
        }

        const data = require('querystring').stringify(params);
        const options = {
            host: 'www.sendcloud.net',
            port: 80,
            path: '/smsapi/send',
            method: 'POST'
        };
        options.path = options.path + '?' + data;

        var req = http.request(options, function (res) {
            var responseStr = '';
            res.on('data', function (chunk) {
                responseStr += chunk;
            });
            res.on('end', function () {
                console.log('-------------------------------------->');
                console.log('sendCloud sms return: \n', responseStr);
                console.log('<--------------------------------------');
            });
        });
        req.end();
    }
}

const { sms: smsApi } = env;

const smsTemplateMap = {
    SIGN_UP: 5806,
    PASSWORD_RETRIEVE: 5807,
    UPDATE_ORDER: 5840,
    AUTH_PASS: 5843,
    AUTH_NO_PASS: 5848
};

const sms = new SmsService(smsApi);

interface ISmsRequest<T> {
    (arg: T): Promise<any>
}

interface ISmsRequestFactory {
    <T>(f: (args: T | T[]) => ISendTo | IXSendTo, templateId: number): ISmsRequest<T>;
}

interface ISmsCaptcha {
    (principal: string, captcha: string): Promise<any>
}

interface ISmsAuthPassArg {
    principal: string
}
interface ISmsAuthNoPassArg {
    principal: string, message: string
}

interface ISmsCaptchaArg {
    principal: string;
    captcha: string;
    order: string;
    message: string;
    type: string;
    no: string;
}

interface ISmsSignUpCaptchaArg extends ISmsCaptchaArg { }
interface ISmsPassportRetrieveCaptchaArg extends ISmsCaptchaArg { }
interface ISmsUpdateOrderCaptchaArg extends ISmsCaptchaArg { }
interface ISmsAuthPassCaptchaArg extends ISmsAuthPassArg { }
interface ISmsAuthNoPassCaptchaArg extends ISmsAuthNoPassArg { }

const smsCaptchaRequestFactory: ISmsRequestFactory = (fn, templateId) => args => {
    console.log('***********************************');
    console.log(templateId, args);
    let { phone, vars } = fn(args);
    let data: ISendSmsByTemplate = {
        templateId: templateId,
        phone,
        vars
    };
    return sms.sendSmsByTemplate(data);
};

/**
 * 手机注册
 * @param {string} principal - 帐号,手机号
 * @param {string} captcha - 验证码
 */
const smsSignUp: ISmsRequest<ISmsSignUpCaptchaArg> = smsCaptchaRequestFactory<ISmsSignUpCaptchaArg>(
    ({ principal, captcha }: ISmsPassportRetrieveCaptchaArg) => ({
        phone: principal,
        vars: '{%code%:' + captcha + '}'
    }), prop('SIGN_UP', smsTemplateMap)
);

/**
 * 手机找回密码
 * @param {string} principal - 帐号,手机号
 * @param {string} captcha - 验证码
 */
const smsPasswordRetrieve: ISmsRequest<ISmsPassportRetrieveCaptchaArg> = smsCaptchaRequestFactory<ISmsPassportRetrieveCaptchaArg>(
    ({ principal, captcha }: ISmsPassportRetrieveCaptchaArg) => ({
        phone: principal,
        vars: '{%code%:' + captcha + '}'
    }), prop('PASSWORD_RETRIEVE', smsTemplateMap)
);

const smsUpdateOrder: ISmsRequest<ISmsUpdateOrderCaptchaArg> = smsCaptchaRequestFactory<ISmsUpdateOrderCaptchaArg>(
    ({ principal, order, message, type, no }: ISmsPassportRetrieveCaptchaArg) => ({
        phone: principal,
        vars: '{%order%:' + order + ',' + '%message%:' + message + ',' + '%type%:' + type + ',' + '%no%:' + no + '}'
    }), prop('UPDATE_ORDER', smsTemplateMap)
)

const smsAuthPass: ISmsRequest<ISmsAuthPassCaptchaArg> = smsCaptchaRequestFactory<ISmsAuthPassCaptchaArg>(
    ({ principal }: ISmsAuthNoPassCaptchaArg) => ({
        phone: principal,
        vars: '{%phone%:' + principal + '}'
    }), prop('AUTH_PASS', smsTemplateMap)
)

const smsAuthNoPass: ISmsRequest<ISmsAuthNoPassCaptchaArg> = smsCaptchaRequestFactory<ISmsAuthNoPassCaptchaArg>(
    ({ principal, message }: ISmsAuthNoPassCaptchaArg) => ({
        phone: principal,
        vars: '{%message%:' + message + '}'
    }), prop('AUTH_NO_PASS', smsTemplateMap)
)
export {
    ISmsCaptchaArg,
    smsSignUp,
    smsPasswordRetrieve,
    smsUpdateOrder,
    smsAuthPass,
    smsAuthNoPass
};
